package com.community.process._1010.common.utils.date;

import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
public class LocalDateUtil {
    /**
     * 英文简写（默认）如：2010-12-01
     */
    public static final String FORMAT_SHORT = "yyyy-MM-dd";
    /**
     * 英文简写（默认）如：2010-12-01
     */
    public static final String FORMAT_DIY = "yyyy/MM/dd";
    /**
     * 英文全称  如：2010-12-01 23:15:06
     */
    public static final String FORMAT_LONG = "yyyy-MM-dd HH:mm:ss";
    /**
     * 精确到毫秒的完整时间    如：yyyy-MM-dd HH:mm:ss.S
     */
    public static final String FORMAT_FULL = "yyyy-MM-dd HH:mm:ss.SSS";
    /**
     * 中文简写  如：2010年12月01日
     */
    public static final String FORMAT_SHORT_CN = "yyyy年MM月dd";
    /**
     * 中文全称  如：2010年12月01日  23时15分06秒
     */
    public static final String FORMAT_LONG_CN = "yyyy年MM月dd日 HH时mm分ss秒";

    /**
     * 精确到毫秒的完整中文时间
     */
    public static final String FORMAT_FULL_CN = "yyyy年MM月dd日  HH时mm分ss秒SSS毫秒";

    private static final List<DateTimeFormatter> FORMATTERS = List.of(
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"),
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"),
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS"),
            DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"),
            DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss.SSS"),
            DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss.SSSSSS"),
            DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒SSSSSS毫秒"),
            DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒SSS毫秒"),
            DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒")
    );

    /**
     * 将 LocalDateTime 对象格式化为字符串，默认格式为 "yyyy-MM-dd HH:mm:ss"
     * @param dateTime LocalDateTime 对象
     * @return 格式化后的日期时间字符串
     */
    public static String format(LocalDateTime dateTime) {
        return format(dateTime, "");
    }

    /**
     * 获取指定日期时间的起始时间字符串
     * @param dateTime LocalDateTime 对象
     * @return 起始时间字符串
     */
    public static String getStartTimeStr(LocalDateTime dateTime) {
        String startTime = null;
        if (dateTime != null) {
            startTime = LocalDateUtil.format(LocalDateUtil.getStartTime(dateTime));
        }
        return startTime;
    }

    /**
     * 获取指定日期时间的结束时间字符串
     * @param dateTime LocalDateTime 对象
     * @return 结束时间字符串
     */
    public static String getEndTimeStr(LocalDateTime dateTime) {
        String endTime = null;
        if (dateTime != null) {
            endTime = LocalDateUtil.format(LocalDateUtil.getEndTime(dateTime));
        }
        return endTime;
    }

    /**
     * 将 LocalDateTime 对象格式化为字符串，可以指定日期时间格式
     * @param dateTime LocalDateTime 对象
     * @param pattern 日期时间格式，如果为空则默认为 "yyyy-MM-dd HH:mm:ss"
     * @return 格式化后的日期时间字符串
     */
    public static String format(LocalDateTime dateTime, String pattern) {
        if (dateTime == null) return null;
        return dateTime.format(DateTimeFormatter.ofPattern(StringUtils.hasLength(pattern) ? pattern : FORMAT_LONG));
    }

    /**
     * 将当前日期时间转换为 LocalDateTime 对象
     * @return 当前日期时间的 LocalDateTime 对象
     */
    public static LocalDateTime parse() {
        return parse(LocalDateTime.now(), "");
    }

    /**
     * 将当前日期时间转换为 LocalDateTime 对象，可以指定日期时间格式
     * @param pattern 日期时间格式，如果为空则默认为 "yyyy-MM-dd HH:mm:ss"
     * @return 当前日期时间的 LocalDateTime 对象
     */
    public static LocalDateTime parse(String pattern) {
        return parse(LocalDateTime.now(), pattern);
    }

    /**
     * 将 LocalDateTime 对象转换为 LocalDateTime 对象，默认格式为 "yyyy-MM-dd HH:mm:ss"
     * @param dateTime LocalDateTime 对象
     * @return 转换后的 LocalDateTime 对象
     */
    public static LocalDateTime parse(LocalDateTime dateTime) {
        return parse(dateTime, "");
    }

    /**
     * 获取指定日期字符串的起始时间
     * @param dateTime 日期字符串
     * @return 起始时间的 LocalDateTime 对象
     */
    public static LocalDateTime getStartTime(String dateTime) {
        if (dateTime == null) return null;
        return getStartTime(toLocalDateTime(dateTime));
    }

    /**
     * 获取指定日期字符串的结束时间
     * @param dateTime 日期字符串
     * @return 结束时间的 LocalDateTime 对象
     */
    public static LocalDateTime getEndTime(String dateTime) {
        if (dateTime == null) return null;
        return getEndTime(toLocalDateTime(dateTime));
    }

    /**
     * 获取指定 Date 对象的起始时间
     * @param date Date 对象
     * @return 起始时间的 LocalDateTime 对象
     */
    public static LocalDateTime getStartTime(Date date) {
        return getStartTime(toLocalDateTime(date));
    }

    /**
     * 获取指定 Date 对象的结束时间
     * @param date Date 对象
     * @return 结束时间的 LocalDateTime 对象
     */
    public static LocalDateTime getEndTime(Date date) {
        return getEndTime(toLocalDateTime(date));
    }

    /**
     * 获取指定日期时间的起始时间
     * @param dateTime LocalDateTime 对象
     * @return 起始时间的 LocalDateTime 对象
     */
    public static LocalDateTime getStartTime(LocalDateTime dateTime) {
        if (dateTime == null) return null;
        return LocalDateTime.of(dateTime.toLocalDate(), LocalTime.MIN);
    }

    /**
     * 获取指定日期时间的结束时间
     * @param dateTime LocalDateTime 对象
     * @return 结束时间的 LocalDateTime 对象
     */
    public static LocalDateTime getEndTime(LocalDateTime dateTime) {
        if (dateTime == null) return null;
        return LocalDateTime.of(dateTime.toLocalDate(), LocalTime.MAX);
    }

    /**
     * 获取指定日期时间所在月份的第一天
     * @param dateTime LocalDateTime 对象
     * @return 所在月份第一天的 LocalDateTime 对象
     */
    public static LocalDateTime getEveryMonthFirstDay(LocalDateTime dateTime) {
        return LocalDateTime.of(LocalDate.from(dateTime.with(TemporalAdjusters.firstDayOfMonth())), LocalTime.MIN);
    }

    /**
     * 获取指定日期时间所在月份的最后一天
     * @param dateTime LocalDateTime 对象
     * @return 所在月份最后一天的 LocalDateTime 对象
     */
    public static LocalDateTime getEveryMonthLastDay(LocalDateTime dateTime) {
        return parse(LocalDateTime.of(LocalDate.from(dateTime.with(TemporalAdjusters.lastDayOfMonth())), LocalTime.MAX));
    }

    /**
     * 获取指定日期时间所在年份的第一天
     * @param dateTime LocalDateTime 对象
     * @return 所在年份第一天的 LocalDateTime 对象
     */
    public static LocalDateTime getEveryYearFirstDay(LocalDateTime dateTime) {
        return LocalDateTime.of(LocalDate.from(dateTime.with(TemporalAdjusters.firstDayOfYear())), LocalTime.MIN);
    }

    /**
     * 获取指定日期时间所在年份的最后一天
     * @param dateTime LocalDateTime 对象
     * @return 所在年份最后一天的 LocalDateTime 对象
     */
    public static LocalDateTime getEveryYearLastDay(LocalDateTime dateTime) {
        return parse(LocalDateTime.of(LocalDate.from(dateTime.with(TemporalAdjusters.lastDayOfYear())), LocalTime.MAX));
    }


    /**
     * 获取指定日期时间所在月份的第一天
     * @param dateTime LocalDateTime 对象
     * @return 所在月份第一天的 LocalDateTime 对象
     */
    public static LocalDateTime firstDayOfMonth(LocalDateTime dateTime) {
        return dateTime.with(TemporalAdjusters.firstDayOfMonth());
    }

    /**
     * 将 LocalDateTime 对象转换为 LocalDateTime 对象，可以指定日期时间格式
     * @param dateTime LocalDateTime 对象
     * @param pattern 日期时间格式，如果为空则默认为 "yyyy-MM-dd HH:mm:ss"
     * @return 转换后的 LocalDateTime 对象
     */
    public static LocalDateTime parse(LocalDateTime dateTime, String pattern) {
        if (dateTime == null) return null;
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        String dateTimeStr = dateTime.format(formatter);
        return toLocalDateTime(dateTimeStr,pattern);
    }

    /**
     * 将 Date 对象转换为 LocalDateTime 对象
     * @param date Date 对象
     * @return 转换后的 LocalDateTime 对象
     */
    public static LocalDateTime toLocalDateTime(Date date) {
        if (date == null) return null;
        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
    }

    /**
     * 将 LocalDateTime 对象转换为 Date 对象
     * @param localDateTime LocalDateTime 对象
     * @return 转换后的 Date 对象
     */
    public static Date toDate(LocalDateTime localDateTime) {
        if (localDateTime == null) return null;
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * LocalDateTime 转 时间戳(秒级别) +8
     *
     * @param localDateTime 时间
     * @return Long
     * @author Zbs
     */
    public static Long localDateTimeToSecond(LocalDateTime localDateTime) {
        if (localDateTime == null) return null;
        return localDateTime.toEpochSecond(ZoneOffset.ofHours(8));
    }

    /**
     * LocalDateTime 转 时间戳(秒级别)
     *
     * @param localDateTime 时间
     * @param timeZone 时区
     * @return Long
     * @author Zbs
     */
    public static Long localDateTimeToSecond(LocalDateTime localDateTime,Integer timeZone) {
        return localDateTime.toEpochSecond(ZoneOffset.ofHours(timeZone));
    }

    /**
     * LocalDateTime 转 时间戳(毫秒级别) +8
     *
     * @param localDateTime 时间
     * @return Long
     * @author Zbs
     */
    public static Long localDateTimeToMilliseconds(LocalDateTime localDateTime) {
        return localDateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
    }

    /**
     * LocalDateTime 转 时间戳(毫秒级别)
     *
     * @param localDateTime 时间
     * @param timeZone 时区
     * @return Long
     * @author Zbs
     */
    public static Long localDateTimeToMilliseconds(LocalDateTime localDateTime,Integer timeZone) {
        return localDateTime.toInstant(ZoneOffset.ofHours(timeZone)).toEpochMilli();
    }

    /**
     * 将字符串日期转换为 LocalDateTime
     * @param time 日期字符串
     * @return LocalDateTime 对象，如果转换失败则返回 null
     */
    public static LocalDateTime toLocalDateTime(String time) {
        if (!StringUtils.hasLength(time)) {
            log.warn("toLocalDateTime 接收到的日期时间字符串为空或仅包含空白字符，无法解析");
            return null;
        }

        if (time.length() == 10) {
            try {
                LocalDate date = LocalDate.parse(time, DateTimeFormatter.ISO_LOCAL_DATE);
                return date.atStartOfDay();
            } catch (DateTimeParseException e) {
                log.error("toLocalDateTime LocalDate 解析日期失败，入参时间：{}", time);
            }
        }

        for (DateTimeFormatter formatter : FORMATTERS) {
            try {
                return LocalDateTime.parse(time, formatter);
            } catch (DateTimeParseException ignored) {
                log.info("toLocalDateTime LocalDateTime 解析日期时间失败，尝试下一个格式器，入参时间：{}", time);
            }
        }

        log.error("toLocalDateTime 无法解析日期时间，入参时间：{}", time);
        return null;
    }

    /**
     * 将字符串日期转换为 LocalDateTime
     * @param time 日期字符串
     * @return LocalDateTime 对象，如果转换失败则返回 null
     */
    public static LocalDateTime toLocalDateTime(String time, String pattern) {
        if (time == null) return null;
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        return LocalDateTime.parse(time, formatter);
    }

    /**
     * 计算时间差
     *
     * @param beginTime 开始时间
     * @param endTime   结束时间
     * @return Duration
     */
    public static Duration between(LocalTime beginTime, LocalTime endTime) {
        return Duration.between(beginTime, endTime);
    }

    /**
     * 计算时间差
     *
     * @param beginTime 开始时间
     * @param endTime   结束时间
     * @return Duration
     */
    public static Duration between(LocalDateTime beginTime, LocalDateTime endTime) {
        return Duration.between(beginTime, endTime);
    }

    /**
     * 计算时间差
     *
     * @param beginTime 开始时间
     * @param endTime   结束时间
     * @return Duration
     */
    public static Duration between(LocalDate beginTime, LocalDate endTime) {
        return Duration.between(beginTime, endTime);
    }

    /**
     * 当前时间是否在时间指定范围内<br>
     *
     * @param time      被检查的时间
     * @param beginTime 起始时间
     * @param endTime   结束时间
     * @return 是否在范围内
     * @since 3.0.8
     */
    public static boolean isIn(LocalTime time, LocalTime beginTime, LocalTime endTime) {
        return beginTime.isBefore(time) && endTime.isAfter(time);
    }


    /**
     * 当前时间是否在时间指定范围内<br>
     *
     * @param time      被检查的时间
     * @param beginTime 起始时间
     * @param endTime   结束时间
     * @return 是否在范围内
     * @since 3.0.8
     */
    public static boolean isIn(LocalDateTime time, LocalDateTime beginTime, LocalDateTime endTime) {
        return beginTime.isBefore(time) && endTime.isAfter(time);
    }


    /**
     * 当前时间是否在时间指定范围内<br>
     *
     * @param time      被检查的时间
     * @param beginTime 起始时间
     * @return 是否在范围内
     * @since 3.0.8
     */
    public static boolean isBefore(LocalTime time, LocalTime beginTime) {
        return beginTime.isBefore(time);
    }

    /**
     * 当前时间是否在时间指定范围内<br>
     *
     * @param time      被检查的时间
     * @param beginTime 起始时间
     * @return 是否在范围内
     * @since 3.0.8
     */
    public static boolean isBefore(LocalDateTime time, LocalDateTime beginTime) {
        return beginTime.isBefore(time);
    }

    /**
     * 当前时间是否在时间指定范围内<br>
     *
     * @param time    被检查的时间
     * @param endTime 结束时间
     * @return 是否在范围内
     * @since 3.0.8
     */
    public static boolean isAfter(LocalTime time, LocalTime endTime) {
        return endTime.isAfter(time);
    }


    /**
     * 当前时间是否在时间指定范围内<br>
     *
     * @param time    被检查的时间
     * @param endTime 结束时间
     * @return 是否在范围内
     * @since 3.0.8
     */
    public static boolean isAfter(LocalDateTime time, LocalDateTime endTime) {
        return endTime.isAfter(time);
    }


    /**
     * 时间戳转 LocalDateTime
     * @param timestamp 时间戳
     * @return LocalDateTime
     */
    public static LocalDateTime toLocalDateTime(Long timestamp) {
        if (timestamp == null) {
            return null;
        }
        return toLocalDateTime(timestamp,8,true);
    }

    /**
     * 时间戳转 LocalDateTime
     * @param timestamp 时间戳
     * @param isMillis true 毫秒时间戳  false 秒时间戳
     * @return LocalDateTime
     */
    public static LocalDateTime toLocalDateTime(Long timestamp, boolean isMillis) {
        if (timestamp == null) {
            return null;
        }
        return toLocalDateTime(timestamp,8,isMillis);
    }

    /**
     * 时间戳转 LocalDateTime
     * @param timestamp 时间戳
     * @param timeZone 时区
     * @param isMillis true 毫秒时间戳  false 秒时间戳
     * @return LocalDateTime
     */
    public static LocalDateTime toLocalDateTime(Long timestamp, Integer timeZone, boolean isMillis) {
        if (timestamp == null) {
            return null;
        }
        Instant instant = isMillis ? Instant.ofEpochMilli(timestamp) : Instant.ofEpochSecond(timestamp);
        return instant.atZone(ZoneOffset.ofHours(timeZone)).toLocalDateTime();
    }

    /**
     * 将 LocalDateTime 转换为秒级别的时间戳
     * @param dateTime LocalDateTime 对象
     * @param timeZone 时区
     * @return 秒级别的时间戳
     */
    public static long toTimestampInSeconds(LocalDateTime dateTime, Integer timeZone) {
        return dateTime.toEpochSecond(ZoneOffset.ofHours(timeZone));
    }

    /**
     * 将 LocalDateTime 转换为秒级别的时间戳，默认时区为东八区
     * @param dateTime LocalDateTime 对象
     * @return 秒级别的时间戳
     */
    public static long toTimestampInSeconds(LocalDateTime dateTime) {
        return toTimestampInSeconds(dateTime,8);
    }

    /**
     * 将 LocalDateTime 转换为毫秒级别的时间戳
     * @param dateTime LocalDateTime 对象
     * @param timeZone 时区
     * @return 毫秒级别的时间戳
     */
    public static long toTimestampInMilliseconds(LocalDateTime dateTime, Integer timeZone) {
        return dateTime.toInstant(ZoneOffset.ofHours(timeZone)).toEpochMilli();
    }

    /**
     * 将 LocalDateTime 转换为毫秒级别的时间戳，默认时区为东八区
     * @param dateTime LocalDateTime 对象
     * @return 毫秒级别的时间戳
     */
    public static long toTimestampInMilliseconds(LocalDateTime dateTime) {
        return toTimestampInMilliseconds(dateTime,8);
    }

    /**
     * 根据起始时间、结束时间、时间类型和间隔参数，返回时间区间列表
     * 间隔时间为1
     *
     * @param startTime 起始时间
     * @param endTime   结束时间
     * @param timeType  时间类型，包括年、月和日
     * @return 包含多个时间区间的列表
     */
    public static List<DateRange> getIntervalTimeList(LocalDateTime startTime, LocalDateTime endTime, TimeTypeEnum timeType) {
        return getIntervalTimeList(startTime, endTime, timeType, 1L);
    }

    /**
     * 根据起始时间、结束时间、时间类型和间隔参数，返回时间区间列表
     *
     * @param startTime   起始时间
     * @param endTime     结束时间
     * @param timeType    时间类型，包括年、月和日
     * @param intervalNum 间隔参数，表示时间区间的间隔数
     * @return 包含多个时间区间的列表
     */
    public static List<DateRange> getIntervalTimeList(LocalDateTime startTime, LocalDateTime endTime, TimeTypeEnum timeType, Long intervalNum) {
        if (intervalNum == null || intervalNum < 1) {
            intervalNum = 1L;
        }

        List<DateRange> intervalTimeList = new ArrayList<>();
        LocalDateTime currentStartTime = startTime;
        LocalDateTime currentEndTime;

        while (currentStartTime.isBefore(endTime)) {
            switch (timeType) {
                case YEAR:
                    currentEndTime = currentStartTime.plusYears(intervalNum).minusSeconds(1);
                    break;
                case MONTH:
                    currentEndTime = currentStartTime.plusMonths(intervalNum).minusSeconds(1);
                    break;
                case DAYS:
                    LocalDate currentDate = currentStartTime.toLocalDate();
                    LocalDate nextDate = currentDate.plusDays(intervalNum);
                    currentEndTime = nextDate.atStartOfDay().minusSeconds(1);
                    if (currentEndTime.isAfter(endTime)) {
                        currentEndTime = endTime;
                    }
                    break; // 在这里实际上可以省略，因为有一个代码块
                case HOUR:
                    currentEndTime = currentStartTime.plusHours(intervalNum).minusSeconds(1);
                    break;
                case MINUTES:
                    currentEndTime = currentStartTime.plusMinutes(intervalNum).minusSeconds(1);
                    break;
                case SECONDS:
                    currentEndTime = currentStartTime.plusSeconds(intervalNum).minusSeconds(1);
                    break;
                default:
                    throw new IllegalArgumentException("Invalid time type: " + timeType);
            }

            // 确保 currentEndTime 不超过 endTime
            if (currentEndTime.isAfter(endTime)) {
                currentEndTime = endTime;
            }

            intervalTimeList.add(new DateRange(currentStartTime, currentEndTime));
            currentStartTime = currentEndTime.plusSeconds(1);
        }

        return intervalTimeList;
    }

    /**
     * 获取指定日期范围内的所有日期
     * @param startDate 开始日期
     * @param endDate 结束日期
     * @param intervalNum 日期间隔
     * @return 日期列表
     */
    public static List<LocalDate> getBetweenLocalDate(LocalDate startDate, LocalDate endDate, Long intervalNum) {
        LocalDateTime startTime = LocalDateTime.of(startDate, LocalTime.MIN);
        LocalDateTime endTime = LocalDateTime.of(endDate, LocalTime.MAX);
        return getBetweenLocalDateTime(startTime, endTime, TimeTypeEnum.DAYS, intervalNum)
                .stream()
                .map(LocalDateTime::toLocalDate)
                .collect(Collectors.toList());
    }

    /**
     * 获取指定日期时间范围内的所有日期时间
     * @param startTime 开始日期时间
     * @param endTime 结束日期时间
     * @param timeTypeEnum 时间类型（年、月、日、小时、分钟、秒）
     * @param intervalNum 日期时间间隔
     * @return 日期时间列表
     */
    public static List<LocalDateTime> getBetweenLocalDateTime(LocalDateTime startTime, LocalDateTime endTime, TimeTypeEnum timeTypeEnum, Long intervalNum) {
        List<LocalDateTime> result = new ArrayList<>();
        LocalDateTime temp = startTime;
        while (temp.isBefore(endTime)) {
            result.add(temp);
            LocalDateTime newTemp;
            switch (timeTypeEnum) {
                case YEAR:
                    newTemp = temp.plusYears(intervalNum);
                    break;
                case MONTH:
                    newTemp = temp.plusMonths(intervalNum);
                    break;
                case DAYS:
                    newTemp = temp.plusDays(intervalNum);
                    break;
                case HOUR:
                    newTemp = temp.plusHours(intervalNum);
                    break;
                case MINUTES:
                    newTemp = temp.plusMinutes(intervalNum);
                    break;
                case SECONDS:
                    newTemp = temp.plusSeconds(intervalNum);
                    break;
                default:
                    throw new IllegalArgumentException("Invalid time type: " + timeTypeEnum);
            }
            temp = newTemp; // 更新 temp 的值
        }
        return result;
    }
}
